package com.auntvt.timo.jpax.core.quick;

import com.auntvt.timo.jpax.core.quick.enums.CriteriaType;
import com.auntvt.timo.jpax.core.quick.interfaces.AtomCriteria;
import com.auntvt.timo.jpax.core.quick.interfaces.JoinCriteria;
import com.auntvt.timo.jpax.core.quick.interfaces.QuickCriteria;
import com.auntvt.timo.jpax.core.quick.node.CriteriaNode;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
 * Quick操作抽象类
 *
 * @author 小懒虫 <auntvt@163.com>
 * @date 2021/7/31
 */
public abstract class AbstractQuick<Children, Container, R> implements QuickCriteria<Children, R>, AtomCriteria<Children>, JoinCriteria<Children, R> {

    private static final long serialVersionUID = -2243722342780606789L;

    /**
     * this变量
     */
    @SuppressWarnings("unchecked")
    protected final Children that = (Children) this;

    /**
     * 查询条件节点列表
     */
    protected List<CriteriaNode> criteriaNodes = new ArrayList<>(16);
    /**
     * 原生容器操作接口
     */
    protected Container quickContainer;

    /**
     * 子类创建一个本身的实例对象
     */
    protected abstract Children instance();

    /**
     * 等于
     * <p>sql：a = ?</p>
     *
     * @param condition 条件表达式
     * @param fieldName 字段名
     * @param value     值
     * @return this
     */
    @Override
    public Children eq(boolean condition, R fieldName, Object value) {
        return addNode(condition, CriteriaType.EQ, fieldName, value);
    }

    /**
     * 不等于
     * <p>sql：a = ?</p>
     *
     * @param condition 条件表达式
     * @param fieldName 字段名
     * @param value     值
     * @return this
     */
    @Override
    public Children ne(boolean condition, R fieldName, Object value) {
        return addNode(condition, CriteriaType.NE, fieldName, value);
    }

    /**
     * 大于
     * <p>sql：a > ?</p>
     *
     * @param condition 条件表达式
     * @param fieldName 字段名
     * @param value     值
     * @return this
     */
    @Override
    public Children gt(boolean condition, R fieldName, Object value) {
        return addNode(condition, CriteriaType.GT, fieldName, value);
    }

    /**
     * 大于等于
     * <p>sql：a >= ?</p>
     *
     * @param condition 条件表达式
     * @param fieldName 字段名
     * @param value     值
     * @return this
     */
    @Override
    public Children ge(boolean condition, R fieldName, Object value) {
        return addNode(condition, CriteriaType.GE, fieldName, value);
    }

    /**
     * 小于
     * <p>sql：a < ?</p>
     *
     * @param condition 条件表达式
     * @param fieldName 字段名
     * @param value     值
     * @return this
     */
    @Override
    public Children lt(boolean condition, R fieldName, Object value) {
        return addNode(condition, CriteriaType.LT, fieldName, value);
    }

    /**
     * 小于等于
     * <p>sql：a <= ?</p>
     *
     * @param condition 条件表达式
     * @param fieldName 字段名
     * @param value     值
     * @return this
     */
    @Override
    public Children le(boolean condition, R fieldName, Object value) {
        return addNode(condition, CriteriaType.LE, fieldName, value);
    }

    /**
     * 模糊查询
     * <p>sql：a like ?</p>
     *
     * @param condition 条件表达式
     * @param fieldName 字段名
     * @param value     值
     * @return this
     */
    @Override
    public Children like(boolean condition, R fieldName, String value) {
        return addNode(condition, CriteriaType.LIKE, fieldName, value);
    }

    /**
     * 模糊查询-两边
     * <p>sql：a like %?%</p>
     *
     * @param condition 条件表达式
     * @param fieldName 字段名
     * @param value     值
     * @return this
     */
    @Override
    public Children likeBoth(boolean condition, R fieldName, String value) {
        return addNode(condition, CriteriaType.LIKE_BOTH, fieldName, value);
    }

    /**
     * 模糊查询-左查询
     * <p>sql：a like %?</p>
     *
     * @param condition 条件表达式
     * @param fieldName 字段名
     * @param value     值
     * @return this
     */
    @Override
    public Children likeLeft(boolean condition, R fieldName, String value) {
        return addNode(condition, CriteriaType.LIKE_LEFT, fieldName, value);
    }

    /**
     * 模糊查询-右查询
     * <p>sql：a like ?%</p>
     *
     * @param condition 条件表达式
     * @param fieldName 字段名
     * @param value     值
     * @return this
     */
    @Override
    public Children likeRight(boolean condition, R fieldName, String value) {
        return addNode(condition, CriteriaType.LIKE_RIGHT, fieldName, value);
    }

    /**
     * 非，模糊查询
     * <p>sql：a not like ?</p>
     *
     * @param condition 条件表达式
     * @param fieldName 字段名
     * @param value     值
     * @return this
     */
    @Override
    public Children notLike(boolean condition, R fieldName, String value) {
        return addNode(condition, CriteriaType.NOT_LIKE, fieldName, value);
    }

    /**
     * 区间查询
     * <p>sql：a between ?1 and ?2</p>
     *
     * @param condition  条件表达式
     * @param fieldName  字段名
     * @param leftValue  左边界值
     * @param rightValue 右边界值
     * @return this
     */
    @Override
    public Children between(boolean condition, R fieldName, Object leftValue, Object rightValue) {
        Object[] boundary = new Object[]{leftValue, rightValue};
        return addNode(condition, CriteriaType.BETWEEN, fieldName, boundary);
    }

    /**
     * 非，区间查询
     * <p>sql：a not between ?1 and ?2</p>
     *
     * @param condition  条件表达式
     * @param fieldName  字段名
     * @param leftValue  左边界值
     * @param rightValue 右边界值
     * @return this
     */
    @Override
    public Children notBetween(boolean condition, R fieldName, Object leftValue, Object rightValue) {
        Object[] boundary = new Object[]{leftValue, rightValue};
        return addNode(condition, CriteriaType.NOT_BETWEEN, fieldName, boundary);
    }

    /**
     * 字段为空
     * <p>sql：a is null</p>
     *
     * @param condition 条件表达式
     * @param fieldName 字段名
     * @return this
     */
    @Override
    public Children isNull(boolean condition, R fieldName) {
        return addNode(condition, CriteriaType.IS_NULL, fieldName, "");
    }

    /**
     * 字段不为空
     * <p>sql：a is null</p>
     *
     * @param condition 条件表达式
     * @param fieldName 字段名
     * @return this
     */
    @Override
    public Children isNotNull(boolean condition, R fieldName) {
        return addNode(condition, CriteriaType.IS_NOT_NULL, fieldName, "");
    }

    /**
     * in 包含的值
     * <p>sql：a in (...)</p>
     *
     * @param condition 条件表达式
     * @param fieldName 字段名
     * @param values    数组值
     * @return this
     */
    @Override
    public Children in(boolean condition, R fieldName, Object... values) {
        return addNode(condition, CriteriaType.IN, fieldName, values);
    }

    /**
     * not in 不包含的值
     * <p>sql：a not in (...)</p>
     *
     * @param condition 条件表达式
     * @param fieldName 字段名
     * @param values    数组值
     * @return this
     */
    @Override
    public Children notIn(boolean condition, R fieldName, Object... values) {
        return addNode(condition, CriteriaType.NOT_IN, fieldName, values);
    }

    /**
     * 并且
     * <p>sql：and (?, ?, ...)</p>
     * <p>用法：and(i -> i.xx().xx())</p>
     *
     * @param condition 条件表达式
     * @param consumer  可使用箭头函数
     * @return this
     */
    @Override
    public Children and(boolean condition, Consumer<Children> consumer) {
        return addAtom(true, CriteriaType.AND, consumer);
    }

    /**
     * 与或
     * <p>sql：or (?, ?, ...)</p>
     * <p>用法：or(i -> i.xx().xx())</p>
     *
     * @param condition 条件表达式
     * @param consumer  可使用箭头函数表达式
     * @return this
     */
    @Override
    public Children or(boolean condition, Consumer<Children> consumer) {
        return addAtom(true, CriteriaType.OR, consumer);
    }

    /**
     * 非，注意只有第一层链表调用有效
     * <p>sql：not xxx</p>
     * <p>用法：not(i -> i.xx())</p>
     *
     * @param condition 条件表达式
     * @param consumer  可使用箭头函数表达式
     * @return this
     */
    @Override
    public Children not(boolean condition, Consumer<Children> consumer) {
        return addAtom(true, CriteriaType.NOT, consumer);
    }

    /**
     * 表关联操作
     * <p>用法：join(i -> i.xx().xx())</p>
     *
     * @param condition    条件表达式
     * @param criteriaType 条件类型
     * @param fieldName    关联字段名
     * @param consumer     可使用箭头函数表达式
     * @return this
     */
    @Override
    public Children join(boolean condition, CriteriaType criteriaType, R fieldName, Consumer<Children> consumer) {
        return addJoin(condition, criteriaType, fieldName, consumer);
    }

    /**
     * 对字段进行类型转换操作
     * <p>sql：cast(字段名 as 转换的类型 )</p>
     *
     * @param asFieldType 字段类型转换
     * @return this
     */
    public Children as(Class<?> asFieldType) {
        CriteriaNode criteriaNode = criteriaNodes.get(criteriaNodes.size() - 1);
        criteriaNode.setAsFieldType(asFieldType);
        return that;
    }

    /**
     * 原生容器
     *
     * @param quickContainer 容器接口
     * @return this
     */
    public Children container(Container quickContainer) {
        this.quickContainer = quickContainer;
        return that;
    }

    /**
     * 节点操作
     *
     * @param condition    条件表达式
     * @param criteriaType 条件类型
     * @param fieldName    字段名
     * @param value        值
     * @return this
     */
    protected Children addNode(boolean condition, CriteriaType criteriaType, R fieldName, Object value) {
        String name = fieldNameToString(fieldName);
        if (condition) {
            criteriaNodes.add(new CriteriaNode(criteriaType, name, value));
        }
        return that;
    }

    /**
     * 原子操作
     *
     * @param condition    条件表达式
     * @param criteriaType 条件类型
     * @param consumer     可使用箭头函数
     * @return this
     */
    protected Children addAtom(boolean condition, CriteriaType criteriaType, Consumer<Children> consumer) {
        if (condition) {
            criteriaNodes.add(new CriteriaNode(criteriaType, getConsumerCriteriaNodes(consumer)));
        }
        return that;
    }

    /**
     * 表关联操作
     * <p>sql：and (?, ?, ...)</p>
     * <p>用法：and(i -> i.xx().xx())</p>
     *
     * @param condition    条件表达式
     * @param criteriaType 条件类型
     * @param fieldName    字段名
     * @param consumer     可使用箭头函数表达式
     * @return this
     */
    protected Children addJoin(boolean condition, CriteriaType criteriaType, R fieldName, Consumer<Children> consumer) {
        String name = fieldNameToString(fieldName);
        if (condition) {
            List<CriteriaNode> consumerCriteriaNodes = getConsumerCriteriaNodes(consumer);
            criteriaNodes.add(new CriteriaNode(criteriaType, name, consumerCriteriaNodes));
        }
        return that;
    }

    /**
     * 表关联操作
     * <p>sql：and (?, ?, ...)</p>
     * <p>用法：and(i -> i.xx().xx())</p>
     *
     * @param condition    条件表达式
     * @param criteriaType 条件类型
     * @param fieldName    字段名
     * @param children     条件节点子集
     * @return this
     */
    protected Children addJoin(boolean condition, CriteriaType criteriaType, R fieldName, List<CriteriaNode> children) {
        String name = fieldNameToString(fieldName);
        if (condition) {
            criteriaNodes.add(new CriteriaNode(criteriaType, name, children));
        }
        return that;
    }

    /**
     * 转换为其他实现方式
     *
     * @param quick AbstractQuick子对象
     * @param <Q>   AbstractQuick子类
     * @return AbstractMatcher实现类
     */
    protected <Q extends AbstractQuick> Q convert(Q quick) {
        quick.criteriaNodes = this.criteriaNodes;
        quick.quickContainer = this.quickContainer;
        return quick;
    }

    /**
     * 将字段名称对象格式化为字符串类型，由子类负责实现
     *
     * @param fieldName 字段名称对象
     * @return 字段名
     */
    protected abstract String fieldNameToString(R fieldName);

    /**
     * 获取Consumer中的条件节点列表
     *
     * @param consumer 可使用箭头函数
     * @return 条件节点列表
     */
    private List<CriteriaNode> getConsumerCriteriaNodes(Consumer<Children> consumer) {
        Children instance = instance();
        consumer.accept(instance);
        @SuppressWarnings("unchecked")
        AbstractQuick<Children, Container, R> abstractQuick = (AbstractQuick) instance;
        return abstractQuick.criteriaNodes;
    }
}
